package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.constant.RedisConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;

/**
 * 登录全局过滤器: 自己手写的过滤器
 * 网关: 默认情况下有9大过滤器(网关自带的) 加上自己手写的 一共10大过滤器
 *   过滤执行顺序 int类型最小的数  0   int类型最大的数
 *   整数越小越优先执行,
 */
@Component
public class LoginGloBalFilter implements GlobalFilter, Ordered {
    //进行路径匹配的
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Value("${gateway.urls}")
    private String[] urls;//  trade.html  pay.html order.html

    //重定向地址
    public static final String REDIRECT_URL = "http://passport.gmall.com/login.html?originUrl=";

    //全局过滤器的执行方法 全局发出的所有请求 都要经过下面的方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String path = request.getURI().getPath();

        //String url = request.getURI().getRawSchemeSpecificPart(); 不带http
        String url = request.getURI().toString();
        //1:校验请求路径是否为内部资源 /inner  不允许网关访问
        boolean match = antPathMatcher.match("/**/inner/**", path);
        if (match){
            //路径匹配 就是内部资源,不允许访问
            this.getVoidMono(response,ResultCodeEnum.PERMISSION);
        }
        //2:校验用户是否登录
        String userId = getUserId(request);
        //3:登录
        //3.1: 有些路径,不登录时也是可以直接访问的
        //3.2: 有些路径,不登录时时不允许访问的
        if (antPathMatcher.match("/**/auth/**",path) && StringUtils.isEmpty(userId)){//异步
            //路径要求必须登录,并且用户还没有登录
            return getVoidMono(response,ResultCodeEnum.LOGIN_AUTH); //适用于异步
        }

        //同步
        for (String u : urls) {
            if (path.indexOf(u) != -1 && StringUtils.isEmpty(userId)){
                //路径要求必须登录 并且 用户还未登录
                //重定向 响应码  303   响应头:重定向的路径
                response.setStatusCode(HttpStatus.SEE_OTHER); //303
                try {
                    response.getHeaders().add(HttpHeaders.LOCATION,
                            REDIRECT_URL+ URLEncoder.encode(url,"utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                //响应: 包含 响应行 响应头 响应码 响应体
                return response.setComplete(); //开始响应
            }
        }


        //4:登录 种类:
        //4,1: Ajax异步的,ajax请求不带cookie,需要将cookie中的令牌转移到请求头中
        //4.2 同步的: window.location.href = url 带cookie cookie里面有令牌
        //传递真实用户id
        if (!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId);
        }
        //传递临时用户Id
        String userTempId = this.getUserTempId(request);
        if (!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId);
        }

        //5.放行
        return chain.filter(exchange);


    }

    //获取临时用户id
    private String getUserTempId(ServerHttpRequest request) {
        //6. 获取临时用户的id
        //1.从请求头中获取
        String userTempId = request.getHeaders().getFirst("userTempId");
        if (StringUtils.isEmpty(userTempId)){
            //1.2从cookie中获取
            HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
            if (null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }

    //统一返回值
    public Mono<Void> getVoidMono(ServerHttpResponse response,ResultCodeEnum resultCodeEnum){
        Result<Object> result = Result.build(null, resultCodeEnum);
        String json = JSONObject.toJSONString(result); //"{code:209,message:没有权限}"
        DataBuffer dataBuffer = response.bufferFactory().wrap(json.getBytes());
        //设置响应的编码
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);

        return response.writeWith(Mono.just(dataBuffer));
    }

    //2:获取用户的id
    private String getUserId(ServerHttpRequest request) {
        //1.获取token :登录方式有两种:Ajax的token在请求头中  同步登录方式在cookie中
        //1.1 从请求头中获取
        String token = request.getHeaders().getFirst("token");
        if (StringUtils.isEmpty(token)){
            //如果请求头中没有在cookie中
            HttpCookie httpCookie = request.getCookies().getFirst("token");
            if (null != httpCookie){
                token = httpCookie.getValue();
            }
        }

        if (!StringUtils.isEmpty(token)){
            //token不为空,就去缓存中获取userId
            String key = RedisConst.USER_KEY_PREFIX + token;
            Boolean flag = redisTemplate.hasKey(key);
            if (flag){
                return (String) redisTemplate.opsForValue().get(key);
            }
        }
        return null;
    }

    @Override
    public int getOrder() {
        //过滤顺序 第四个执行
        return 0;
    }

    @Autowired
    private RedisTemplate redisTemplate;
}
